﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;

namespace SageKing.Core.Extensions;

public static class ChangeTypeHelper
{
    public static T ChangeType<T>(this object value)
    {
        return ChangeType<T>(value, null);
    }

    public static T ChangeType<T>(this object value, IFormatProvider provider)
    {
        var t = typeof(T);

        if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
        {
            if (value == null)
            {
                return default;
            }

            t = Nullable.GetUnderlyingType(t);
        }

        if (provider == null) return (T)Convert.ChangeType(value, t);
        return (T)Convert.ChangeType(value, t, provider);
    }

    public static bool IsTypeNullable<T>()
    {
        return IsTypeNullable(typeof(T));
    }

    public static bool IsTypeNullable(Type type)
    {
        return Nullable.GetUnderlyingType(type) != null;
    }

    public static Type GetNullableType<T>()
    {
        var type = typeof(T);
        type = Nullable.GetUnderlyingType(type) ?? type;
        if (type.IsValueType)
            return typeof(Nullable<>).MakeGenericType(type);
        else
            return type;
    }

    public static Type GetUnderlyingType<T>()
    {
        var type = typeof(T);
        Type targetType;
        if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            targetType = Nullable.GetUnderlyingType(type);
        }
        else
        {
            targetType = type;
        }
        return targetType;
    }

    public static Type GetUnderlyingType(this Type type)
    {
        Type targetType;
        if (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
        {
            targetType = Nullable.GetUnderlyingType(type);
        }
        else
        {
            targetType = type;
        }
        return targetType;
    }
    public static bool IsNumericType(this Type type)
    {
        return type != null
            && Type.GetTypeCode(type)
                   is TypeCode.Byte
                   or TypeCode.Decimal
                   or TypeCode.Double
                   or TypeCode.Int16
                   or TypeCode.Int32
                   or TypeCode.Int64
                   or TypeCode.SByte
                   or TypeCode.Single
                   or TypeCode.UInt16
                   or TypeCode.UInt32
                   or TypeCode.UInt64;
    }

    public static bool IsDateType(this Type type)
    {
        return type != null && (type == typeof(DateTime)
            || type == typeof(DateTimeOffset)
#if NET6_0_OR_GREATER
            || type == typeof(TimeOnly)
            || type == typeof(DateOnly)
#endif
            );
    }

    public static bool IsArrayOrList(this Type that) => that != null && (that.IsArray || typeof(IList).IsAssignableFrom(that));

    public static bool IsEnumerable(this Type that) => that != null && (that.IsArray || typeof(IEnumerable).IsAssignableFrom(that));
    public static bool IsUserDefinedClass(this Type thta) =>
        thta.IsClass && thta.Namespace != null && !thta.Namespace.StartsWith("System");
    public static bool IsEnum(this Type that) => that != null && that.BaseType != null && that.BaseType.FullName.Contains(TypeConsts.enum12);
    public static bool IsString(this Type that) => that != null && that.FullName.Contains(TypeConsts.string0);
    public static bool IsBool(this Type that) => that != null && that.FullName.Contains(TypeConsts.bool11);

    /// <summary>
    /// Type: 可选值 string,int,long, double,float, bool, date, datetime, time
    /// </summary>
    /// <param name="typeName">string, int,long, double,float, bool, date, datetime, time</param>
    /// <returns></returns>
    public static Type GetTypeByName(this string typeName)
    {
        switch (typeName)
        {
            case "string":
                return TypeConsts.String;
            case "int":
                return TypeConsts.Int;
            case "long":
                return TypeConsts.Long;
            case "double":
                return TypeConsts.Double;
            case "float":
                return TypeConsts.Float;
            case "bool":
                return TypeConsts.Bool;
            case "date":
            case "datetime":
                return TypeConsts.DateTime;
            case "time":
                return TypeConsts.TimeOnly;
            default:
                return TypeConsts.String;
        }
    }
}